package spel;

import java.util.LinkedList;
import java.util.List;

public class Bord {
	public static final int DIM = 9;
	public static final int SUB = 9;
	private SubBord subBorden[];

	/**
	 * Initialiseert een bord met lege velden en subborden
	 * 
	 * @ensure VA getVeld(x,y).getKnikker()==null
	 */
	public Bord() {
		subBorden = new SubBord[SUB];
		for (int i = 0; i < SUB; i++) {
			subBorden[i] = new SubBord(i);
		}
	}
	
	/**
	 * returns een veld op het bord op basis van xy positie
	 * @param x x positie van het veld
	 * @param y y positie van het veld
	 * @return het veld op positie x,y,null als deze niet binnen het bord valt
	 */
	public Veld getVeld(int x, int y) {
		return(getSubBord(x/SubBord.DIM,y/SubBord.DIM).getVeld(x%SubBord.DIM, y%SubBord.DIM));
	}
	
	/**
	 * returns het veld op het bord op basis van index;
	 * @param index de index van het veld
	 * @return het veld op positie index%Bord.DIM,index/Bord.DIM, null als deze niet binnen het bord valt
	 */
	public Veld getVeld(int index) {
		return getVeld(index%Bord.DIM, index/Bord.DIM);
	}
	
	/**
	 * Returns het SubBord op basis van xy positie
	 * @param x x positie van het subbord
	 * @param y y positie van het subbord
	 * @return het SubBord op index x+y*sqrt(Bord.SUB), null als deze niet binnen het bord valt
	 */
	public SubBord getSubBord(int x, int y) {
		return getSubBord(x + y * (int)Math.sqrt(SUB));
	}
	
	/**
	 * returnd het SubBord op het bord op basis van index;
	 * @param index de index van het SubBord
	 * @return het veld op index index, null als deze niet binnen het bord valt
	 */
	public SubBord getSubBord(int index) {
		if (index < SUB)
			return subBorden[index];
		else
			return null;
	}
	
	/**
	 * Checkt of het veld leeg is
	 * @param x x positie van het Veld
	 * @param y y positie van het Veld
	 * @return true als getVeld(x,y).getKnikker()==null
	 */
	public boolean isLeegVeld(int x, int y) {
		return getVeld(x,y).getKnikker()==null;
	}
	
	/**
	 * Checkt of het veld leeg is
	 * @param index index van het Veld
	 * @return true als getVeld(index).getKnikker()==null
	 */
	public boolean isLeegVeld(int index) {
		return getVeld(index).getKnikker()==null;
	}
	
	/**
	 * Plaats een knikker op de gekozen locatie op het bord.
	 * Verlaagt het aantal vrij knikkers met 1.
	 * @param index de positie index%Bord.DIM,index/Bord.DIM, waar de knikker geplaats moet worden.
	 * @param k De knikker die op de positie geplaatst moet worden
	 * @require isLeegVeld(index)==true && (k.getAantal()>0|| k = null) && index < Bord.DIM*BORD.DIM
	 * @ensure getVeld(index).getKnikker == k
	 */
	public void PlaatsKnikker(int index, Knikker k) {
		PlaatsKnikker(index%Bord.DIM, index/Bord.DIM,k);
	}
	
	/**
	 * Plaats een knikker op de gekozen locatie op het bord.
	 * Verlaagt het aantal vrij knikkers met 1.
	 * @param x de x positie waar de knikker moet worden geplaatst
	 * @param y de y positie waar de knikker moet worden geplaatst
	 * @param k de knikker die op de positie geplaatst moet worden
	 * @require isLeegVeld(index)==true && (k.getAantal()>0|| k = null) && x< Bord.DIM && y<Bord.DIM
	 * @ensure getVeld(x,y).getKnikker == k
	 */
	public void PlaatsKnikker(int x, int y, Knikker k) {
		getVeld(x,y).setKnikker(k);
		if(k!=null)
		k.plaatsKnikker();
	}
	
	/**
	 * Bepaalt of er een speler is die gewonnen heeft
	 * @return Alle spelers met minimaal 1 keer 5 knikkers op een rij
	 */
	public List<Speler> isWinnaar() {
		List<Speler> spelers = new LinkedList<Speler>();
		//een list voor spelers die gewonnen hebben, omdat er meerdere winnaars kunnen zijn
		Knikker[][] knikkers = new Knikker[Bord.DIM][Bord.DIM];
		//Itereert 1 keer door het hele bord heen, buffert steeds de knikker op deze positie
		int[] xcount = new int[Bord.DIM];
		int[][] rcount = new int[Bord.DIM][Bord.DIM];
		int[][] lcount = new int[Bord.DIM][Bord.DIM];
		int ycount = 0;
		
		for(int i = 0; i<Bord.DIM; i ++){
			for(int j = 0; j < Bord.DIM; j++){
				knikkers[i][j] = getVeld(i,j).getKnikker();
				if(i == 0){
					xcount[j] = 1;
					rcount[i][j] = 1;
					lcount[i][j] = 1;
				}else{
					if(knikkers[i][j] != null && knikkers[i][j] == knikkers[i-1][j]){
						xcount[j]++;
						if(xcount[j] == 5 && knikkers[i][j] != null && !spelers.contains(knikkers[i][j].getSpeler())){
							spelers.add(knikkers[i][j].getSpeler());
						}
					}else{
						xcount[j] = 1;
					}
				}
				if(j == 0){
					ycount = 1;
					rcount[i][j] = 1;
				}else{
					if(knikkers[i][j] != null && knikkers[i][j] == knikkers[i][j-1]){
						ycount++;
						if(ycount == 5 && knikkers[i][j] != null && !spelers.contains(knikkers[i][j].getSpeler())){
							spelers.add(knikkers[i][j].getSpeler());
						}
					}else{
						ycount = 1;
					}
				}
				if(j==Bord.DIM-1){
					lcount[i][j] = 1;
				}
				if(i>0 && j<Bord.DIM-1){
					if(knikkers[i][j] != null && knikkers[i][j] == knikkers[i-1][j+1]){
						rcount[i][j] = rcount[i-1][j+1] + 1;
						if(rcount[i][j] == 5 && knikkers[i][j] != null && !spelers.contains(knikkers[i][j].getSpeler())){
							spelers.add(knikkers[i][j].getSpeler());
						}
					}else{
						rcount[i][j] = 1;
					}
				}
				if(i>0 && j>0){
					if(knikkers[i][j] != null && knikkers[i][j] == knikkers[i-1][j-1]){
						lcount[i][j] = lcount[i-1][j-1] + 1;
						if(lcount[i][j] == 5 && knikkers[i][j] != null && !spelers.contains(knikkers[i][j].getSpeler())){
							spelers.add(knikkers[i][j].getSpeler());
						}
					}else{
						lcount[i][j] = 1;
					}
				}				
			}
		}		
		return spelers;		
	}
	
	/**
	 * Kijkt of het bord leeg is
	 * @return false als er nog een leeg vakje op het bord is. anders true
	 */
	public boolean isVol(){
		for(int i = 0; i < Bord.DIM;i++){
			for(int j = 0; j<Bord.DIM; j++){
				if(this.isLeegVeld(i, j)){
					return false;
				}
			}
		}		
		return true;
	}
	
	/**
	 * Kijkt hoeveel buren een knikker heeft van dezelfde kleur
	 * @param x de x positie het vakje waar de knikker in ligt
	 * @param y de y positie het vakje waar de knikker in ligt
	 * @return het aantal knikkers aanliggende dit vakje met dezelfde kleur als de knikker in het vakje, 
	 * 			-1 als het vakje leeg is.
	 */
	public int aantalBuren(int x, int y){
		int aantal = -1;//-1 omdat het middelste 
		Knikker k = getVeld(x,y).getKnikker();
		if(k == null){
			return aantal;
		}
		int xstart,xend,ystart,yend;
		//checkt of het Veld aan de linker kant van het bord ligt
		xstart=(x==0?x:x-1);
		//checkt of het Veld aan de rechter kant van het bord ligt
		xend=(x==Bord.DIM-1?x:x+1);
		//checkt of het Veld aan de bovenkant van het bord ligt
		ystart=(y==0?y:y-1);
		//checkt of het Veld aan de onderkant van het bord ligt
		yend=(y==Bord.DIM-1?y:y+1);
		
		//deze loop gaat door alle buren van het vakje, rekening houdend met de grensen van het bord
		for(int i = xstart;i<=xend;i++){
			for(int j = ystart;j<yend;j++){
				if(getVeld(i,j).getKnikker()==k){
					aantal++;
				}
			}
		}		
		// alle buren van dezelfde kleur
		return aantal;		
	}
	
	/**
	 * Draait het gekozen SubBord in de meegegeven richting
	 * @param index de index van het te draaien SubBord
	 * @param r de richting waarin gedraait moet worden.
	 * @require index < Bord.Sub
	 */
	public void DraaiSubBord(int index, Zet.Richting r) {
		if(r==Zet.Richting.Rechts){
			subBorden[index].draaiRechts();
			subBorden[index].draaiRechts();
		}else{
			subBorden[index].draaiLinks();
			subBorden[index].draaiLinks();
		}
	}
	
	/**
	 * Draait het gekozen SubBord in de meegegeven richting
	 * @param x x positie van het subbord
	 * @param y y positie van het subbord
	 * @param r de richting waarin gedraait moet worden.
	 * @require x+y*3 < Bord.SUB
	 */
	public void DraaiSubBord(int x, int y, Zet.Richting r) {
		DraaiSubBord(x+y*3,r);
	}

}
